﻿using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Collections.Generic;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.AspNetCore.Routing;
using PineLake.Drmwe.Lib.ResultMsg;

namespace RepositoryMultipleDataBases.Filters
{
    /// <summary>
    ///通过(POST PUT)幂等防止重复提交过滤器
    /// </summary>
    public class IdempotentFilter : ActionFilterAttribute
    {
        private readonly IMemoryCache _MemoryCache;
        /// <summary>
        /// 过期时间.默认 10 秒
        /// </summary>
        private int _ExpiressAbsoulteFromSeconds = 10;
        public IdempotentFilter(int expiressAbsoulteFromSeconds, IMemoryCache caching)
        {
            _MemoryCache = caching;
            _ExpiressAbsoulteFromSeconds = expiressAbsoulteFromSeconds;
            //Order = 1;
        }

        public override async void OnActionExecuted(ActionExecutedContext context)
        {
            string HashCodeCacheKey = await GetContextInfoHashCode(context.HttpContext, (ControllerActionDescriptor)context.ActionDescriptor);
            if (!string.IsNullOrEmpty(HashCodeCacheKey))
            {
                if (!_MemoryCache.TryGetValue(HashCodeCacheKey, out _))
                {
                    //设置缓存有效期默认10秒(10秒内防止重新提交)
                    _MemoryCache.Set(HashCodeCacheKey, 1, new MemoryCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromSeconds(ChkExpTime())));
                }
            }
        }

        public override async void OnActionExecuting(ActionExecutingContext context)
        {
            string HashCodeCacheKey = await GetContextInfoHashCode(context.HttpContext, (ControllerActionDescriptor)context.ActionDescriptor);
            if (!string.IsNullOrEmpty(HashCodeCacheKey))
            {
                if (_MemoryCache.TryGetValue(HashCodeCacheKey, out _))
                {
                    //Console.WriteLine($"执行前Key: {HashCodeCacheKey}");
                    //Console.WriteLine($"执行前CacheV: {CacheValue}");
                    //设置断路返回内容
                    var Result = new ApiResultMsg()
                    {
                        StatusCode = StatusCodes.Status405MethodNotAllowed,
                        Info = $"当前数据 {ChkExpTime()} 秒内,不要重复提交!"
                    };
                    context.HttpContext.Response.StatusCode = StatusCodes.Status405MethodNotAllowed;
                    context.Result = new JsonResult(Result);
                    return;
                }
            }
        }
        /// <summary>
        /// 设置是是大于 0 小于整形的最大数
        /// </summary>
        /// <returns></returns>
        private int ChkExpTime()
        {
            if (_ExpiressAbsoulteFromSeconds <= 0 || _ExpiressAbsoulteFromSeconds > int.MaxValue)
            {
                _ExpiressAbsoulteFromSeconds = 10;
            }
            return _ExpiressAbsoulteFromSeconds;
        }
        /// <summary>
        /// 生成哈希值
        /// </summary>
        /// <param name="ActionHttpContext">HttpContext</param>
        /// <param name="Action">Action</param>
        /// <returns></returns>
        private async Task<string> GetContextInfoHashCode(HttpContext ActionHttpContext, ControllerActionDescriptor Action)
        {
            var RequestInfoHashCode = "";
            var Method = ActionHttpContext.Request.Method;
            if (Method == "POST" || Method == "PUT")
            {
                #region 读取Body内容
                string BodyStr = "";
                if (ActionHttpContext.Request.Body.Length > 0)
                {
                    ActionHttpContext.Request.Body.Seek(0, SeekOrigin.Begin);
                    StreamReader BodyStream = new StreamReader(ActionHttpContext.Request.Body);
                    BodyStr = await BodyStream.ReadToEndAsync();
                    ActionHttpContext.Request.Body.Seek(0, SeekOrigin.Begin);
                }
                #endregion
                var ClientIp = ActionHttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString();
                //兼容 Nginx 只能获取到本地IP的方法
                if (ActionHttpContext.Request.Headers.ContainsKey("X-Forwarded-For"))
                {
                    var NginxIp = ActionHttpContext.Request.Headers["X-Forwarded-For"].ToString();
                    ClientIp = string.IsNullOrEmpty(NginxIp) == true ? ClientIp : NginxIp;
                }
                RequestInfoHashCode = String.Format("{0:X}", (Action.ControllerName + Action.ActionName + BodyStr + ClientIp).GetHashCode()); //生成哈希值
            }
            return RequestInfoHashCode;
        }
    }
}
